#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "Sock.hpp"

using namespace ns_sock;

namespace ns_selectserver
{
    #define NUM 128
    class SelectServer
    {
    private:
        uint16_t _port;
        int _listen_sock;
        int _maxfd;
        int _fd_arrays[NUM];
    public:
        SelectServer(uint16_t port = 8081)
            :_port(port)
            ,_listen_sock(-1)
        {
            for (int i = 0; i < NUM; ++i) {
                _fd_arrays[i] = -1;
            }
        }

        void InitServer() {
            _listen_sock = Sock::Socket();
            Sock::Bind(_listen_sock, _port);
            Sock::Listen(_listen_sock);
            // 把listen_sock加入用户层面的读关心数组里去
            _fd_arrays[0] = _listen_sock; 
        }

        void HandlerSelect(const fd_set &readsets) {
            for (int i = 0; i < NUM; ++i) {
                if (FD_ISSET(_fd_arrays[i], &readsets)) {
                    // 该fd就绪了
                    if (_fd_arrays[i] == _listen_sock) {
                        // 该fd是listen的sock，表示有新的sock到来
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);  
                        int sock = accept(_listen_sock, (struct sockaddr *)&peer, &len);
                        // 将该新来的fd添加进用户层面的读关心数组
                        int index = -1;
                        for (int j = 0; j < NUM; ++j) {
                            if (_fd_arrays[j] != -1) continue;
                            index = j;
                            break;
                        }
                        if (index == -1) {
                            std::cout << "_fd_arrays满了 " << std::endl;
                            close(sock);
                        }
                        _fd_arrays[index] = sock;
                        std::cout << "新sock到来， sock：" << sock << std::endl;
                    } else {
                        // 该fd是就绪表示底层有数据到来
                        char buffer[1024];
                        int s = read(_fd_arrays[i], buffer, sizeof(buffer) - 1);
                        if (s > 0) {
                            buffer[s] = 0;
                            std::cout << "client say# " << buffer << std::endl;
                        } else if (s == 0) {
                            std::cout << "client quit, sock: " << _fd_arrays[i] << std::endl;
                            close(_fd_arrays[i]);
                            _fd_arrays[i] = -1;
                        } else {
                            std::cout << "read error " << std::endl;
                            close(_fd_arrays[i]);
                            _fd_arrays[i] = -1;
                        }
                    }
                }
            }
        }
        void Loop() {
            while (true) {
                fd_set readsets;
                FD_ZERO(&readsets);
                timeval timeout = {1, 0};
                for (int i = 0; i < NUM; ++i) {
                    if (_fd_arrays[i] != -1) {
                        FD_SET(_fd_arrays[i], &readsets);
                        if (_fd_arrays[i] > _maxfd) {
                            _maxfd = _fd_arrays[i];
                        }
                    }
                }
                int num = select(_maxfd + 1, &readsets, nullptr, nullptr, /*&timeout*/ nullptr);
                switch (num)
                {
                case 0:
                    std::cout << "timeout " << std::endl;
                    break;
                case -1:
                    std::cout << "select error " << std::endl;
                    break;
                default:
                    // 有链接到来后，如果不进行处理，也就是测试时没有使用HandlerSelect，就会刷屏“有数据”，因为没有对缓冲区的数据进行处理，每次select检测都会知道有fd就绪
                    // std::cout << "有数据" << std::endl;
                    HandlerSelect(readsets);
                    break;
                }
            }
        }

        ~SelectServer() {
            if (_listen_sock >= 0) {
                close(_listen_sock);
            }
        }
    };
}